from fastapi import BackgroundTasks
from sqlalchemy.orm import Session
from sqlalchemy.sql import func
import traceback
import sys
import json
from datetime import datetime
from fastapi.encoders import jsonable_encoder
from app.models.account import AccountStatement, SubAccount, Account
from app.models.merchant import Merchant
from app.models import PayOrder
from app.schemas.merchant import MerchantFee
from app.schemas.trans import FeOrderParam, InitPayOrder, QueryOrderStatus
from app.schemas import account as acc_scm
from app.services import merchant as merc_svc
from app.services import account as acc_svc
from app.services import factor as factor_svc
from app import services
from app import schemas, tasks

from app import conf
from app.common.enums.resp_enum import ErrTypes
from app.common import exceptions as excs
from app.common.exceptions import GenericException
from app.utils import id_util, risk_util, api_util
from app import utils
from app.common.enums.bz_enum import OrderType, PayType, OrderStatus, AccountingType
from .order import is_merc_order_exist

import logging
logger = logging.getLogger(__name__)


PAY_TYPE_DAIFU = PayType.DAIFU.value[0]
'''
PAY_PRODUCT_CODE = 'PF_DAIFU'
PAY_CHANNEL_CODE = 'PF_DAIFU'
pay_channel_code = PAY_CHANNEL_CODE
'''
#PAY_CHANNEL_NAME = '浦发代付'
DAIFU_URL = conf.SERVCIE_URLS.get('daifu-fe-pf')
ACCOUNT_CENTER_URL = conf.SERVCIE_URLS.get('account-center') 
RISK_CENTER_URL = conf.SERVCIE_URLS.get('risk-center') 

from app import ctx_conf

#def insert_daifu_order(db: Session, params: dict):
def init_daifu_order(db: Session, background_tasks: BackgroundTasks, params: dict):
    logger.info('>> 发起代付交易')
    #app = ctx_conf.get('app')
    try:
        ######################## 参数检查 ########################
        logger.info('--> 参数校验')
        if not params.get('merc_no'):
            raise GenericException(error_type=ErrTypes.bz_param_validation_error)
        if not params.get('merc_order_no'):
            raise GenericException(error_type=ErrTypes.bz_param_validation_error)
        if not params.get('amount'):
            raise GenericException(error_type=ErrTypes.bz_param_validation_error)
        if not params.get('order_type'):
            raise GenericException(error_type=ErrTypes.bz_param_validation_error)
        if not params.get('user_type'):
            raise GenericException(error_type=ErrTypes.bz_param_validation_error)
        if not params.get('card_type'):
            raise GenericException(error_type=ErrTypes.bz_param_validation_error)
        if not params.get('bank_code'):
            raise GenericException(error_type=ErrTypes.bz_param_validation_error)
        if not params.get('counter_acc_no'):
            raise GenericException(error_type=ErrTypes.bz_param_validation_error)
        if not params.get('counter_acc_name'):
            raise GenericException(error_type=ErrTypes.bz_param_validation_error)
        if not params.get('currency'):
            raise GenericException(error_type=ErrTypes.bz_param_validation_error)
        
        if is_merc_order_exist(db, params.get('merc_no'), params.get('merc_order_no')):
            #logger.error('商户订单号重复')
            raise GenericException(error_type=ErrTypes.trans_merc_order_no_repeat)
        ######################## 业务校验 ########################

        order = InitPayOrder(**params)
        merc_no = order.merc_no
        trans_amount = order.amount
        merchant = merc_svc.get_merchant_info(db=db, params={'merc_no': merc_no})
        if not merchant:
            raise GenericException(ErrTypes.db_record_not_found, message='商户信息有误')
        elif merchant.status != 1:
            raise GenericException(ErrTypes.merc_status_invalid)
        #merc_fee: MerchantFee = merc_svc.get_merchant_fee(db=db, merc_no=merc_no, product_code=PAY_PRODUCT_CODE)
        # 路由获取商户费率
        merc_fee = utils.get_merc_fee_info(db, merc_no, PAY_TYPE_DAIFU)
        pay_channel_code = merc_fee.pay_channel_code
        product_code = merc_fee.product_code
        pay_product = services.get_pay_product(db, product_code)
        pay_channel_name = pay_product.pay_channel_name
        #pay_product = services.get_pay_product_by_channel_code(db, product_code=pay_channel_code)
        #pay_channel_code = pay_product.pay_channel_code
        if not merc_fee:
            raise GenericException(ErrTypes.fee_not_found, message='商户未开通相关业务')
        fee_mode = merc_fee.fee_mode
        fee_value = merc_fee.fee_value
        factor_no = merchant.factor_no
        factor = factor_svc.get_factor(db, factor_no=factor_no)
        if factor.status != 1:
            raise GenericException(ErrTypes.factor_status_error)
        factor_fee = factor_svc.get_factor_fee(db=db, factor_no=factor_no, product_code=product_code, fee_mode=fee_mode)
        if not factor:
            #logger.error('代理不存在')
            raise GenericException(ErrTypes.factor_not_found, message='上级代理信息有误，无法进行此操作')
        if not factor_fee:
            #logger.error('上级代理无此费率')
            raise GenericException(ErrTypes.fee_not_found, message='上级代理无此费率')
        
        ######################## 风控校验 ########################
        logger.info('--> 基础风险校验')
        trans_fee = 0
        pay_channel = services.get_pay_channel(db, pay_channel_code)
        risk = risk_util.RiskUtil(db, pay_product, pay_channel, factor=factor, merchant=merchant, 
                                  fee_mode=fee_mode, merc_fee=fee_value, factor_fee=factor_fee.fee_value)
        #fee_check_result = risk.check_merc_fee_risk(fee_mode, fee_value, factor_fee.fee_value)
        risk_result = risk.check_all()
        if risk_result:
            if fee_mode == 1:
                trans_fee = merc_fee.fee_value
            elif fee_mode == 2:
                trans_fee = order.amount * merc_fee.fee_value
            else:
                raise GenericException(ErrTypes.fee_unsupported_mode)
        else:
            raise GenericException(ErrTypes.risk_validation_error)
        
        logger.info('--> 风控服务校验')
        payload = {
            "amount": trans_amount,
            "card_no": order.counter_acc_no
        }
        try:
            risk_result = utils.request_service_api(RISK_CENTER_URL, '/api/v1/risk/rule/parse_trans_rule', payload)
            if risk_result.get('code') != '000000' or not risk_result.get('data'):
                fail_msg = risk_result.get('message') or ErrTypes.risk_rule_validation_fail.value[1]
                raise GenericException(ErrTypes.risk_rule_validation_fail, message=fail_msg)   
            else:
                logger.info('风控校验通过')
        except GenericException as err:
            raise GenericException(err.error_type, message=err.message, data=err.data)
        except Exception as err:
            raise GenericException(ErrTypes.risk_rule_validation_fail) 

        
        ####################### 插入订单 #########################
        order_no = id_util.gen_order_no()
        order_type = OrderType.PAY.value[0]
        pay_type = PayType.DAIFU.value[0]
        logger.info('--> 插入订单')
        new_order = PayOrder(
            order_no=order_no,
            order_type=order_type,
            pay_type=pay_type,
            merc_no=merc_no,
            merc_name=merchant.merc_name,
            factor_no=merchant.factor_no,
            amount=order.amount,
            fee=trans_fee,
            fee_mode=fee_mode,
            product_code=product_code,
            pay_chn_code=pay_channel_code,
            currency=order.currency,
            pay_chn_name=pay_channel_name,
            status=OrderStatus.INIT.value[0],
            is_profit_processed='N'
        )
        new_order.merc_order_no = order.merc_order_no
        new_order.card_no = order.counter_acc_no
        new_order.card_type = order.card_type
        new_order.bank_code = order.bank_code
        if params.get('user_type'):
            new_order.account_type = params.get('user_type')
        
        # 订单扩展信息
        services.add_order_ext_info(db, order_no)
        
        # 订单通知回调初始记录
        if params.get('notify_url'):
            services.order.add_order_notice(db, new_order=new_order, params=params)
        
        ######################## 账户调账 #########################
        # 需调账的总金额
        total_amount = order.amount + trans_fee
        # 账户调账(资金冻结)
        logger.info(f'--> 商户 [{merc_no}] 账户调账')
        #acc_info = acc_svc.get_full_account_info(db, {'merc_no': merc_no})
        acc_info = acc_svc.get_db_account_info(db, merc_no)
        account: Account = acc_info[0]
        balance_acc: SubAccount = acc_info[1]['balance']
        pending_acc: SubAccount = acc_info[1]['pending']
        logger.info(f'[调账前] 账户余额: {balance_acc.balance}, 在途金额: {pending_acc.balance}, 待调整金额: {total_amount}')
        if balance_acc.balance >= total_amount:
            vres1 = utils.PayUtil.verify_balance(balance_acc)
            vres2 = utils.PayUtil.verify_balance(pending_acc)
            if vres1 and vres2:
                balance_acc.balance -= total_amount
                pending_acc.balance += total_amount
                balance_acc.balance_sign = utils.PayUtil.sign_balance(balance_acc)
                pending_acc.balance_sign = utils.PayUtil.sign_balance(pending_acc)
            else:
                raise GenericException(ErrTypes.acc_invalid_signature)
        else:
            raise GenericException(ErrTypes.acc_no_enough_balance)
        logger.info(f'[调账后] 账户余额: {balance_acc.balance}, 在途金额: {pending_acc.balance}, 已调整金额: {total_amount}')

        # 插入流水
        balance_acc_stmt = AccountStatement(
            stmt_id=utils.id_util.gen_trans_id(prefix='T'),
            account_no=account.account_no,
            sub_acc_no=balance_acc.sub_acc_no,
            sub_type=balance_acc.sub_type,
            total_amount=-total_amount,
            trans_amount=order.amount,
            trans_fee=trans_fee,
            balance=balance_acc.balance,
            order_no=new_order.order_no,
            accounting_type=AccountingType.ORDER_TRANSIT.value[0]
        )
        pending_acc_stmt = AccountStatement(
            stmt_id=utils.id_util.gen_trans_id(prefix='T'),
            account_no=account.account_no,
            sub_acc_no=pending_acc.sub_acc_no,
            sub_type=pending_acc.sub_type,
            total_amount=total_amount,
            trans_amount=order.amount,
            trans_fee=trans_fee,
            balance=pending_acc.balance,
            order_no=new_order.order_no,
            accounting_type=AccountingType.ORDER_TRANSIT.value[0]
        )

        ####################### 事务提交 ###########################
        new_order.create_time = datetime.now()
        db.add(new_order)
        db.add(balance_acc)
        db.add(pending_acc)
        db.add(balance_acc_stmt)
        db.add(pending_acc_stmt)
        db.commit()
        db.refresh(new_order)
    

        ####################### 交易前置参数 ########################

        fe_params = FeOrderParam(
            amount=utils.amt.scale_down_100x(params['amount']),
            bank_code=params.get('bank_code'),
            card_type=params.get('card_type'),
            user_type=params.get('user_type'),
            counter_acc_name=params.get('counter_acc_name'),
            counter_acc_no=params.get('counter_acc_no'),
            currency=params.get('currency'),
            order_no=order_no,
            inter_bank_flag=utils.param.get_inter_bank_flag(pay_channel_code ,params.get('bank_code'))
        )
        #fe_params['order_no'] = order_no
        #fe_params['inter_bank_flag'] = utils.param.get_inter_bank_flag(pay_channel_code ,params.get('bank_code'))
        #fe_params['amount'] = utils.amt.scale_down_100x(params['amount'])
        print(f'fe_params: {fe_params.model_dump_json()}')
        
        #################### 发送前置 ########################
        channel_conf = conf.PAY_CHANNEL_CONFIG.get(pay_channel_code)
        if not channel_conf:
            raise GenericException(ErrTypes.chn_no_channel_conf)
        fe_url = channel_conf.get('api_url')
        resp_data = {}
        try:
            fe_resp = api_util.request_fe_api(fe_url, '/api/collPay', payload=fe_params.model_dump())
            #fe_resp = {}
            logger.info('response: %s' % fe_resp)
            '''
            tasks.order_init_after_task(db=db, new_order=new_order, factor=factor,
                                        factor_fee=factor_fee,
                                        fee_mode=fee_mode, trans_fee=trans_fee,
                                        pay_channel_code=pay_channel_code)
            '''
            # 更新订单信息
            new_order.status = fe_resp.get('trans_result')
            new_order.up_chn_order_no = fe_resp.get('chn_sys_seq_no')
            new_order.chn_resp_code = fe_resp.get('chn_status_code')
            new_order.chn_resp_msg = fe_resp.get('chn_resp_msg')
            db.add(new_order)
            db.commit()
            db.refresh(new_order)
            # 订单后续任务
            background_tasks.add_task(tasks.order_init_after_task, 
                                        db=db, new_order=new_order, params=params,
                                        factor=factor, factor_fee=factor_fee,
                                        fee_mode=fee_mode, trans_fee=trans_fee,
                                        pay_channel_code=pay_channel_code)
            # 状态判断
            if new_order.status in (2, 9):
                resp_data = {
                    'chn_status_code': new_order.chn_resp_code,
                    'chn_resp_msg': new_order.chn_resp_msg
                }
                order_resp = schemas.NewOrderResp.model_validate(new_order)
                #order_resp.merc_order_no = new_order.merc_order_no
                raise GenericException(ErrTypes.chn_trans_error, data=order_resp.model_dump())
            # new_order.status = OrderStatus.PENDING.value[0]
            else:
                return new_order
            
        except GenericException as err:
            #logger.error(err)
            #exception = GenericException(err.error_type, message=err.message, data=err.data)
            raise GenericException(err.error_type, message=err.message, data=err.data)
        except Exception as err:
            logger.error('通道交易出错, 订单已初始化, 状态未更新')
            exc_type, exc_value, exc_obj = sys.exc_info()
            traceback.print_exception(exc_type,exc_value,exc_obj,limit=3,file=sys.stdout)
            raise GenericException(ErrTypes.chn_trans_error)
    except GenericException as err:
        #logger.error(err)
        #exception = GenericException(err.error_type, message=err.message, data=err.data)
        raise GenericException(err.error_type, message=err.message, data=err.data)
    except Exception as err:
        logger.error('发起代付交易出错')
        exc_type, exc_value, exc_obj = sys.exc_info()
        traceback.print_exception(exc_type,exc_value,exc_obj,limit=3,file=sys.stdout)
        raise GenericException(ErrTypes.trans_create_fail)
    

'''订单查询'''
def query_daifu_order_status(db: Session, params: dict):
    payload = {
        'origTranDate': params.get('orig_trans_date'),
        'oldAskSysRplSqlNo': params.get('orig_order_no'),
        #'tranDate': utils.get_time_now(4),
        #'rqsSeqNo': utils.gen_order_no()
    }
    try:
        if not params.get('orig_trans_date'):
            raise GenericException(error_type=ErrTypes.bz_param_validation_error)
        if not params.get('orig_order_no'):
            raise GenericException(error_type=ErrTypes.bz_param_validation_error)
        res = api_util.request_fe_api(DAIFU_URL, '/api/orderStatus', payload=params)
        return res
    except GenericException as err:
        logger.error('Error: %s' % err.message)
        raise GenericException(err.error_type)
    except Exception as err:
        logger.error('发起代付交易出错')
        exc_type, exc_value, exc_obj = sys.exc_info()
        traceback.print_exception(exc_type,exc_value,exc_obj,limit=3,file=sys.stdout)
        raise GenericException(ErrTypes.trans_create_fail)

